//===--- Index.cpp --------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/index/Index.h"
#include "polarphp/ast/AstContext.h"
#include "polarphp/ast/Comment.h"
#include "polarphp/ast/Decl.h"
#include "polarphp/ast/Expr.h"
#include "polarphp/ast/Module.h"
#include "polarphp/ast/ParameterList.h"
#include "polarphp/ast/InterfaceConformance.h"
#include "polarphp/ast/SourceFile.h"
#include "polarphp/ast/TypeRepr.h"
#include "polarphp/ast/Types.h"
#include "polarphp/ast/UsrGeneration.h"
#include "polarphp/basic/SourceMgr.h"
#include "polarphp/basic/StringExtras.h"
#include "polarphp/ide/SourceEntityWalker.h"
#include "polarphp/markup/Markup.h"
#include "polarphp/sema/IDETypeChecking.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include <tuple>

using namespace polar;
using namespace polar::index;

static bool
printArtificialName(const polar::AbstractStorageDecl *ASD, AccessorKind AK, llvm::raw_ostream &OS) {
   switch (AK) {
      case AccessorKind::Get:
         OS << "getter:" << ASD->getFullName();
         return false;
      case AccessorKind::Set:
         OS << "setter:" << ASD->getFullName();
         return false;
      case AccessorKind::DidSet:
         OS << "didSet:" << ASD->getFullName();
         return false;
      case AccessorKind::WillSet:
         OS << "willSet:" << ASD->getFullName() ;
         return false;

      case AccessorKind::Address:
      case AccessorKind::MutableAddress:
      case AccessorKind::Read:
      case AccessorKind::Modify:
         return true;
   }

   llvm_unreachable("Unhandled AccessorKind in switch.");
}

static bool printDisplayName(const polar::ValueDecl *D, llvm::raw_ostream &OS) {
   if (!D->hasName() && !isa<ParamDecl>(D)) {
      auto *FD = dyn_cast<AccessorDecl>(D);
      if (!FD)
         return true;
      return printArtificialName(FD->getStorage(), FD->getAccessorKind(), OS);
   }

   OS << D->getFullName();
   return false;
}

static bool isMemberwiseInit(polar::ValueDecl *D) {
   if (auto AFD = dyn_cast<AbstractFunctionDecl>(D))
      return AFD->getBodyKind() == AbstractFunctionDecl::BodyKind::MemberwiseInitializer;
   return false;
}

static SourceLoc getLocForExtension(ExtensionDecl *D) {
   // Use the 'End' token of the range, in case it is a compound name, e.g.
   //   extension A.B {}
   // we want the location of 'B' token.
   if (auto *repr = D->getExtendedTypeRepr()) {
      return repr->getSourceRange().end;
   }
   return SourceLoc();
}

namespace {
// Adapter providing a common interface for a SourceFile/Module.
class SourceFileOrModule {
   llvm::PointerUnion<SourceFile *, ModuleDecl *> SFOrMod;

public:
   SourceFileOrModule(SourceFile &SF) : SFOrMod(&SF) {}
   SourceFileOrModule(ModuleDecl &Mod) : SFOrMod(&Mod) {}

   SourceFile *getAsSourceFile() const {
      return SFOrMod.dyn_cast<SourceFile *>();
   }

   ModuleDecl *getAsModule() const { return SFOrMod.dyn_cast<ModuleDecl *>(); }

   ModuleDecl &getModule() const {
      if (auto SF = SFOrMod.dyn_cast<SourceFile *>())
         return *SF->getParentModule();
      return *SFOrMod.get<ModuleDecl *>();
   }

   ArrayRef<FileUnit *> getFiles() const {
      return SFOrMod.is<SourceFile *>() ? *SFOrMod.getAddrOfPtr1()
                                        : SFOrMod.get<ModuleDecl *>()->getFiles();
   }

   StringRef getFilename() const {
      if (auto *SF = SFOrMod.dyn_cast<SourceFile *>())
         return SF->getFilename();
      return SFOrMod.get<ModuleDecl *>()->getModuleFilename();
   }

   void
   getImportedModules(SmallVectorImpl<ModuleDecl::ImportedModule> &Modules) const {
      ModuleDecl::ImportFilter ImportFilter;
      ImportFilter |= ModuleDecl::ImportFilterKind::Public;
      ImportFilter |= ModuleDecl::ImportFilterKind::Private;
      ImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly;

      if (auto *SF = SFOrMod.dyn_cast<SourceFile *>()) {
         SF->getImportedModules(Modules, ImportFilter);
      } else {
         SFOrMod.get<ModuleDecl *>()->getImportedModules(Modules, ImportFilter);
      }
   }
};

struct IndexedWitness {
   ValueDecl *Member;
   ValueDecl *Requirement;
};

class IndexPHPAstWalker : public SourceEntityWalker {
   IndexDataConsumer &IdxConsumer;
   SourceManager &SrcMgr;
   unsigned BufferID;
   bool enableWarnings;

   bool IsModuleFile = false;
   bool isSystemModule = false;
   struct Entity {
      Decl *D;
      SymbolInfo SymInfo;
      SymbolRoleSet Roles;
      SmallVector<IndexedWitness, 6> ExplicitWitnesses;
      SmallVector<SourceLoc, 6> RefsToSuppress;
   };
   SmallVector<Entity, 6> EntitiesStack;
   SmallVector<Expr *, 8> ExprStack;
   SmallVector<const AccessorDecl *, 4> ManuallyVisitedAccessorStack;
   bool Cancelled = false;

   struct NameAndUSR {
      StringRef USR;
      StringRef name;
   };
   typedef llvm::PointerIntPair<Decl *, 3> DeclAccessorPair;
   llvm::DenseMap<void *, NameAndUSR> nameAndUSRCache;
   llvm::DenseMap<DeclAccessorPair, NameAndUSR> accessorNameAndUSRCache;
   StringScratchSpace stringStorage;

   bool getNameAndUSR(ValueDecl *D, ExtensionDecl *ExtD,
                      StringRef &name, StringRef &USR) {
      auto &result = nameAndUSRCache[ExtD ? (Decl*)ExtD : D];
      if (result.USR.empty()) {
         SmallString<128> storage;
         {
            llvm::raw_svector_ostream OS(storage);
            if (ExtD) {
               if (ide::printExtensionUSR(ExtD, OS))
                  return true;
            } else {
               if (ide::printValueDeclUSR(D, OS))
                  return true;
            }
            result.USR = stringStorage.copyString(OS.str());
         }

         storage.clear();
         {
            llvm::raw_svector_ostream OS(storage);
            printDisplayName(D, OS);
            result.name = stringStorage.copyString(OS.str());
         }
      }

      name = result.name;
      USR = result.USR;
      return false;
   }

   bool getModuleNameAndUSR(ModuleEntity Mod, StringRef &name, StringRef &USR) {
      auto &result = nameAndUSRCache[Mod.getOpaqueValue()];
      if (result.USR.empty()) {
         SmallString<128> storage;
         {
            llvm::raw_svector_ostream OS(storage);
            if (ide::printModuleUSR(Mod, OS))
               return true;
            result.USR = stringStorage.copyString(OS.str());
         }
         storage.clear();
         {
            llvm::raw_svector_ostream OS(storage);
            OS << Mod.getFullName();
            result.name = stringStorage.copyString(OS.str());
         }
      }
      name = result.name;
      USR = result.USR;
      return false;
   }

   bool getPseudoAccessorNameAndUSR(AbstractStorageDecl *D, AccessorKind AK, StringRef &Name, StringRef &USR) {
      assert(static_cast<int>(AK) < 0x111 && "AccessorKind too big for pair");
      DeclAccessorPair key(D, static_cast<int>(AK));
      auto &result = accessorNameAndUSRCache[key];
      if (result.USR.empty()) {
         SmallString<128> storage;
         {
            llvm::raw_svector_ostream OS(storage);
            if (ide::printAccessorUSR(D, AK, OS))
               return true;
            result.USR = stringStorage.copyString(OS.str());
         }

         storage.clear();
         {
            llvm::raw_svector_ostream OS(storage);
            printArtificialName(D, AK, OS);
            result.name = stringStorage.copyString(OS.str());
         }
      }

      Name = result.name;
      USR = result.USR;
      return false;
   }

   bool addRelation(IndexSymbol &Info, SymbolRoleSet RelationRoles, Decl *D) {
      assert(D);
      auto Match = std::find_if(Info.Relations.begin(), Info.Relations.end(),
                                [D](IndexRelation R) { return R.decl == D; });
      if (Match != Info.Relations.end()) {
         Match->roles |= RelationRoles;
         Info.roles |= RelationRoles;
         return false;
      }

      StringRef Name, USR;
      SymbolInfo SymInfo = getSymbolInfoForDecl(D);

      if (SymInfo.Kind == SymbolKind::Unknown)
         return true;
      if (auto *ExtD = dyn_cast<ExtensionDecl>(D)) {
         NominalTypeDecl *NTD = ExtD->getExtendedNominal();
         if (getNameAndUSR(NTD, ExtD, Name, USR))
            return true;
      } else {
         if (getNameAndUSR(cast<ValueDecl>(D), /*ExtD=*/nullptr, Name, USR))
            return true;
      }

      Info.Relations.push_back(IndexRelation(RelationRoles, D, SymInfo, Name, USR));
      Info.roles |= RelationRoles;
      return false;
   }

public:
   IndexPHPAstWalker(IndexDataConsumer &IdxConsumer, AstContext &Ctx,
                       unsigned BufferID = -1)
      : IdxConsumer(IdxConsumer), SrcMgr(Ctx.SourceMgr), BufferID(BufferID),
        enableWarnings(IdxConsumer.enableWarnings()) {}

   ~IndexPHPAstWalker() override {
      assert(Cancelled || EntitiesStack.empty());
      assert(Cancelled || ManuallyVisitedAccessorStack.empty());
   }

   void visitModule(ModuleDecl &Mod);
   void visitDeclContext(DeclContext *DC);

private:
   bool visitImports(SourceFileOrModule Mod,
                     llvm::SmallPtrSetImpl<ModuleDecl *> &Visited);

   bool handleSourceOrModuleFile(SourceFileOrModule SFOrMod);

   bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
      // Do not handle unavailable decls.
      if (AvailableAttr::isUnavailable(D))
         return false;

      if (!handleCustomAttrInitRefs(D))
         return false;

      if (auto *AD = dyn_cast<AccessorDecl>(D)) {
         if (ManuallyVisitedAccessorStack.empty() ||
             ManuallyVisitedAccessorStack.back() != AD)
            return false; // already handled as part of the var decl.
      }
      if (auto *VD = dyn_cast<ValueDecl>(D)) {
         if (!report(VD))
            return false;
      }
      if (auto *ED = dyn_cast<ExtensionDecl>(D))
         return reportExtension(ED);
      return true;
   }

   bool walkToDeclPost(Decl *D) override {
      if (Cancelled)
         return false;

      if (getParentDecl() == D)
         return finishCurrentEntity();

      return true;
   }

   /// Report calls to the initializers of property wrapper types on wrapped properties.
   ///
   /// These may be either explicit:
   ///     `\@Wrapper(initialialValue: 42) var x: Int`
   /// or implicit:
   ///     `\@Wrapper var x = 10`
   bool handleCustomAttrInitRefs(Decl * D) {
      for (auto *customAttr : D->getAttrs().getAttributes<CustomAttr, true>()) {
         if (customAttr->isImplicit())
            continue;

         auto &Loc = customAttr->getTypeLoc();
         if (auto *semanticInit = dyn_cast_or_null<CallExpr>(customAttr->getSemanticInit())) {
            if (auto *CD = semanticInit->getCalledValue()) {
               if (!shouldIndex(CD, /*IsRef*/true))
                  continue;
               IndexSymbol Info;
               if (initIndexSymbol(CD, Loc.getLoc(), /*IsRef=*/true, Info))
                  continue;
               Info.roles |= (unsigned)SymbolRole::Call;
               if (semanticInit->isImplicit())
                  Info.roles |= (unsigned)SymbolRole::Implicit;
               if (!startEntity(CD, Info, /*IsRef=*/true) || !finishCurrentEntity())
                  return false;
            }
         }
      }
      return true;
   }

   void handleMemberwiseInitRefs(Expr *E) {
      if (!isa<ConstructorRefCallExpr>(E))
         return;

      auto *DeclRef = dyn_cast<DeclRefExpr>(cast<ConstructorRefCallExpr>(E)->getFn());
      if (!DeclRef || !isMemberwiseInit(DeclRef->getDecl()))
         return;

      // get label locations
      auto *MemberwiseInit = DeclRef->getDecl();
      std::vector<SourceLoc> LabelLocs;
      ArrayRef<Identifier> Labels;
      auto NameLoc = DeclRef->getNameLoc();
      if (NameLoc.isCompound()) {
         size_t LabelIndex = 0;
         SourceLoc ArgLoc;
         while ((ArgLoc = NameLoc.getArgumentLabelLoc(LabelIndex++)).isValid()) {
            LabelLocs.push_back(ArgLoc);
         }
         Labels = MemberwiseInit->getFullName().getArgumentNames();
      } else if (auto *CallParent = dyn_cast_or_null<CallExpr>(getParentExpr())) {
         LabelLocs = CallParent->getArgumentLabelLocs();
         Labels = CallParent->getArgumentLabels();
      }

      if (LabelLocs.empty())
         return;

      assert(Labels.size() == LabelLocs.size());

      // match labels to properties
      auto *TypeContext =
         MemberwiseInit->getDeclContext()->getSelfNominalTypeDecl();
      if (!TypeContext || !shouldIndex(TypeContext, false))
         return;

      unsigned CurLabel = 0;
      for (auto Member : TypeContext->getMembers()) {
         auto Prop = dyn_cast<VarDecl>(Member);
         if (!Prop)
            continue;

         if (!Prop->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
            continue;

         if (CurLabel == LabelLocs.size())
            break;

         if (Labels[CurLabel] != Prop->getName())
            continue;

         IndexSymbol Info;
         if (initIndexSymbol(Prop, LabelLocs[CurLabel++], /*IsRef=*/true, Info))
            continue;
         if (startEntity(Prop, Info, /*IsRef=*/true))
            finishCurrentEntity();
      }
   }

   bool walkToExprPre(Expr *E) override {
      if (Cancelled)
         return false;
      ExprStack.push_back(E);

      handleMemberwiseInitRefs(E);
      return true;
   }

   bool walkToExprPost(Expr *E) override {
      if (Cancelled)
         return false;
      assert(ExprStack.back() == E);
      ExprStack.pop_back();
      return true;
   }

   bool visitDeclReference(ValueDecl *D, CharSourceRange Range,
                           TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T,
                           ReferenceMetaData Data) override {
      SourceLoc Loc = Range.getStart();

      if (isRepressed(Loc) || Loc.isInvalid())
         return true;

      IndexSymbol Info;

      if (Data.isImplicit)
         Info.roles |= (unsigned)SymbolRole::Implicit;

      if (CtorTyRef)
         if (!reportRef(CtorTyRef, Loc, Info, Data.AccKind))
            return false;
      if (!reportRef(D, Loc, Info, Data.AccKind))
         return false;

      // If this is a reference to a property wrapper backing property or
      // projected value, report a reference to the wrapped property too (i.e.
      // report an occurrence of `foo` in `_foo` and '$foo').
      if (auto *VD = dyn_cast<VarDecl>(D)) {
         if (auto *Wrapped = VD->getOriginalWrappedProperty()) {
            assert(Range.getByteLength() > 1 &&
                   (Range.str().front() == '_' || Range.str().front() == '$'));
            auto AfterDollar = Loc.getAdvancedLoc(1);
            reportRef(Wrapped, AfterDollar, Info, None);
         }
      }

      return true;
   }

   bool visitModuleReference(ModuleEntity Mod, CharSourceRange Range) override {
      SourceLoc Loc = Range.getStart();

      if (Loc.isInvalid())
         return true;

      IndexSymbol Info;
      std::tie(Info.line, Info.column, Info.offset) = getLineColAndOffset(Loc);
      Info.roles |= (unsigned)SymbolRole::Reference;
      Info.symInfo = getSymbolInfoForModule(Mod);
      getModuleNameAndUSR(Mod, Info.name, Info.USR);

      if (auto Container = getContainingDecl())
         addRelation(Info, (unsigned)SymbolRole::RelationContainedBy, Container);

      if (!IdxConsumer.startSourceEntity(Info)) {
         Cancelled = true;
         return true;
      }

      return finishSourceEntity(Info.symInfo, Info.roles);
   }

   Decl *getParentDecl() const {
      if (!EntitiesStack.empty())
         return EntitiesStack.back().D;
      return nullptr;
   }

   Decl *getContainingDecl() const {
      for (const auto &Entity: EntitiesStack) {
         if (isa<AbstractFunctionDecl>(Entity.D) &&
             (Entity.Roles & (SymbolRoleSet)SymbolRole::Definition)) {
            return Entity.D;
         }
      }
      return nullptr;
   }

   void repressRefAtLoc(SourceLoc Loc) {
      if (Loc.isInvalid()) return;
      assert(!EntitiesStack.empty());
      EntitiesStack.back().RefsToSuppress.push_back(Loc);
   }

   bool isRepressed(SourceLoc Loc) const {
      if (EntitiesStack.empty() || Loc.isInvalid())
         return false;
      auto &Suppressed = EntitiesStack.back().RefsToSuppress;
      return std::find(Suppressed.begin(), Suppressed.end(), Loc) != Suppressed.end();

   }

   Expr *getContainingExpr(size_t index) const {
      if (ExprStack.size() > index)
         return ExprStack.end()[-std::ptrdiff_t(index + 1)];
      return nullptr;
   }

   Expr *getCurrentExpr() const {
      return ExprStack.empty() ? nullptr : ExprStack.back();
   }

   Expr *getParentExpr() const {
      return getContainingExpr(1);
   }


   bool report(ValueDecl *D);
   bool reportExtension(ExtensionDecl *D);
   bool reportRef(ValueDecl *D, SourceLoc Loc, IndexSymbol &Info,
                  Optional<AccessKind> AccKind);
   bool reportImplicitConformance(ValueDecl *witness, ValueDecl *requirement,
                                  Decl *container);

   bool startEntity(Decl *D, IndexSymbol &Info, bool IsRef);
   bool startEntityDecl(ValueDecl *D);

   bool reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit, SymbolRoleSet Relations, Decl *Related);
   bool reportRelatedTypeRef(const TypeLoc &Ty, SymbolRoleSet Relations, Decl *Related);
   bool reportInheritedTypeRefs(ArrayRef<TypeLoc> Inherited, Decl *Inheritee);
   NominalTypeDecl *getTypeLocAsNominalTypeDecl(const TypeLoc &Ty);

   bool reportPseudoGetterDecl(VarDecl *D) {
      return reportPseudoAccessor(D, AccessorKind::Get, /*IsRef=*/false,
                                  D->getLoc());
   }
   bool reportPseudoSetterDecl(VarDecl *D) {
      return reportPseudoAccessor(D, AccessorKind::Set, /*IsRef=*/false,
                                  D->getLoc());
   }
   bool reportPseudoAccessor(AbstractStorageDecl *D, AccessorKind AccKind,
                             bool IsRef, SourceLoc Loc);

   bool finishCurrentEntity() {
      Entity CurrEnt = EntitiesStack.pop_back_val();
      assert(CurrEnt.SymInfo.Kind != SymbolKind::Unknown);
      return finishSourceEntity(CurrEnt.SymInfo, CurrEnt.Roles);
   }

   bool finishSourceEntity(SymbolInfo symInfo, SymbolRoleSet roles) {
      if (!IdxConsumer.finishSourceEntity(symInfo, roles)) {
         Cancelled = true;
         return false;
      }
      return true;
   }

   bool initIndexSymbol(ValueDecl *D, SourceLoc Loc, bool IsRef,
                        IndexSymbol &Info);
   bool initIndexSymbol(ExtensionDecl *D, ValueDecl *ExtendedD, SourceLoc Loc,
                        IndexSymbol &Info);
   bool initFuncDeclIndexSymbol(FuncDecl *D, IndexSymbol &Info);
   bool initFuncRefIndexSymbol(ValueDecl *D, SourceLoc Loc, IndexSymbol &Info);
   bool initVarRefIndexSymbols(Expr *CurrentE, ValueDecl *D, SourceLoc Loc,
                               IndexSymbol &Info, Optional<AccessKind> AccKind);

   bool indexComment(const Decl *D);

   std::tuple<unsigned, unsigned, Optional<unsigned>>
   getLineColAndOffset(SourceLoc Loc) {
      if (Loc.isInvalid())
         return std::make_tuple(0, 0, None);
      auto lineAndColumn = SrcMgr.getLineAndColumn(Loc, BufferID);
      unsigned offset = SrcMgr.getLocOffsetInBuffer(Loc, BufferID);
      return std::make_tuple(lineAndColumn.first, lineAndColumn.second, offset);
   }

   bool shouldIndex(ValueDecl *D, bool IsRef) const {
      if (D->isImplicit() && isa<VarDecl>(D) && IsRef) {
         // Bypass the implicit VarDecls introduced in CaseStmt bodies by using the
         // canonical VarDecl for these checks instead.
         D = cast<VarDecl>(D)->getCanonicalVarDecl();
      }

      if (D->isImplicit() && !isa<ConstructorDecl>(D))
         return false;

      // Do not handle non-public imported decls.
      if (IsModuleFile && !D->isAccessibleFrom(nullptr))
         return false;

      if (!IdxConsumer.indexLocals() && isLocalSymbol(D))
         return isa<ParamDecl>(D) && !IsRef &&
                D->getDeclContext()->getContextKind() != DeclContextKind::AbstractClosureExpr;

      if (D->isPrivateStdlibDecl())
         return false;

      return true;
   }

   /// Reports all implicit member value decl conformances that \p D introduces
   /// as implicit overrides at the source location of \p D, and returns the
   /// explicit ones so we can check against them later on when visiting them as
   /// members.
   ///
   /// \returns false if AST visitation should stop.
   bool handleWitnesses(Decl *D, SmallVectorImpl<IndexedWitness> &explicitWitnesses);

   void getRecursiveModuleImports(ModuleDecl &Mod,
                                  SmallVectorImpl<ModuleDecl *> &Imports);
   void
   collectRecursiveModuleImports(ModuleDecl &Mod,
                                 llvm::SmallPtrSetImpl<ModuleDecl *> &Visited);

   template <typename F>
   void warn(F log) {
      if (!enableWarnings)
         return;

      SmallString<128> warning;
      llvm::raw_svector_ostream OS(warning);
      log(OS);
   }

   // This maps a module to all its imports, recursively.
   llvm::DenseMap<ModuleDecl *, llvm::SmallVector<ModuleDecl *, 4>> ImportsMap;
};
} // anonymous namespace

void IndexPHPAstWalker::visitDeclContext(DeclContext *Context) {
   IsModuleFile = false;
   isSystemModule = Context->getParentModule()->isSystemModule();
   auto accessor = dyn_cast<AccessorDecl>(Context);
   if (accessor)
      ManuallyVisitedAccessorStack.push_back(accessor);
   walk(Context);
   if (accessor)
      ManuallyVisitedAccessorStack.pop_back();
}

void IndexPHPAstWalker::visitModule(ModuleDecl &Mod) {
   SourceFile *SrcFile = nullptr;
   for (auto File : Mod.getFiles()) {
      if (auto SF = dyn_cast<SourceFile>(File)) {
         auto BufID = SF->getBufferID();
         if (BufID.hasValue() && *BufID == BufferID) {
            SrcFile = SF;
            break;
         }
      }
   }

   if (SrcFile != nullptr) {
      IsModuleFile = false;
      if (!handleSourceOrModuleFile(*SrcFile))
         return;
      walk(*SrcFile);
   } else {
      IsModuleFile = true;
      isSystemModule = Mod.isSystemModule();
      if (!handleSourceOrModuleFile(Mod))
         return;
      walk(Mod);
   }
}

bool IndexPHPAstWalker::handleSourceOrModuleFile(SourceFileOrModule SFOrMod) {
   // Common reporting for TU/module file.

   llvm::SmallPtrSet<ModuleDecl *, 16> Visited;
   return visitImports(SFOrMod, Visited);
}

bool IndexPHPAstWalker::visitImports(
   SourceFileOrModule TopMod, llvm::SmallPtrSetImpl<ModuleDecl *> &Visited) {
   // Dependencies of the stdlib module (like SwiftShims module) are
   // implementation details.
   if (TopMod.getModule().isStdlibModule())
      return true;

   bool IsNew = Visited.insert(&TopMod.getModule()).second;
   if (!IsNew)
      return true;

   SmallVector<ModuleDecl::ImportedModule, 8> Imports;
   TopMod.getImportedModules(Imports);

   llvm::SmallPtrSet<ModuleDecl *, 8> Reported;
   for (auto Import : Imports) {
      ModuleDecl *Mod = Import.second;
      bool NewReport = Reported.insert(Mod).second;
      if (!NewReport)
         continue;

      // FIXME: Handle modules with multiple source files; these will fail on
      // getModuleFilename() (by returning an empty path). Note that such modules
      // may be heterogeneous.
      StringRef Path = Mod->getModuleFilename();
      if (Path.empty() || Path == TopMod.getFilename())
         continue; // this is a submodule.

      Optional<bool> IsClangModuleOpt;
      for (auto File : Mod->getFiles()) {
         switch (File->getKind()) {
            case FileUnitKind::Source:
            case FileUnitKind::Builtin:
               break;
            case FileUnitKind::SerializedAST:
               assert(!IsClangModuleOpt.hasValue() &&
                      "cannot handle multi-file modules");
               IsClangModuleOpt = false;
               break;
            case FileUnitKind::ClangModule:
            case FileUnitKind::DWARFModule:
               assert(!IsClangModuleOpt.hasValue() &&
                      "cannot handle multi-file modules");
               IsClangModuleOpt = true;
               break;
         }
      }
      if (!IsClangModuleOpt.hasValue())
         continue;
      bool IsClangModule = *IsClangModuleOpt;

      if (!IdxConsumer.startDependency(Mod->getName().str(), Path, IsClangModule,
                                       Mod->isSystemModule()))
         return false;
      if (!IsClangModule)
         if (!visitImports(*Mod, Visited))
            return false;
      if (!IdxConsumer.finishDependency(IsClangModule))
         return false;
   }

   return true;
}

bool IndexPHPAstWalker::handleWitnesses(Decl *D, SmallVectorImpl<IndexedWitness> &explicitWitnesses) {
   auto DC = dyn_cast<DeclContext>(D);
   if (!DC)
      return true;

   for (auto *conf : DC->getLocalConformances()) {
      if (conf->isInvalid())
         continue;

      // Ignore self-conformances; they're not interesting to show to users.
      auto normal = dyn_cast<NormalInterfaceConformance>(conf->getRootConformance());
      if (!normal)
         continue;

      normal->forEachValueWitness([&](ValueDecl *req, Witness witness) {
         if (Cancelled)
            return;

         auto *decl = witness.getDecl();
         if (decl == nullptr)
            return;

         if (decl->getDeclContext() == DC) {
            explicitWitnesses.push_back({decl, req});
         } else {
            reportImplicitConformance(decl, req, D);
         }
      });

      normal->forEachTypeWitness(
         [&](AssociatedTypeDecl *assoc, Type type, TypeDecl *typeDecl) {
            if (Cancelled)
               return true;
            if (typeDecl == nullptr)
               return false;

            if (typeDecl->getDeclContext() == DC) {
               explicitWitnesses.push_back({typeDecl, assoc});
            } else {
               // Report the implicit conformance.
               reportImplicitConformance(typeDecl, assoc, D);
            }
            return false;
         });
   }

   if (Cancelled)
      return false;

   return true;
}

bool IndexPHPAstWalker::startEntity(Decl *D, IndexSymbol &Info, bool IsRef) {
   switch (IdxConsumer.startSourceEntity(Info)) {
      case polar::index::IndexDataConsumer::Abort:
         Cancelled = true;
         LLVM_FALLTHROUGH;
      case polar::index::IndexDataConsumer::Skip:
         return false;
      case polar::index::IndexDataConsumer::Continue: {
         SmallVector<IndexedWitness, 6> explicitWitnesses;
         if (!IsRef) {
            if (!handleWitnesses(D, explicitWitnesses))
               return false;
         }
         EntitiesStack.push_back({D, Info.symInfo, Info.roles, std::move(explicitWitnesses), {}});
         return true;
      }
   }

   llvm_unreachable("Unhandled IndexDataConsumer in switch.");
}

bool IndexPHPAstWalker::startEntityDecl(ValueDecl *D) {
   if (!shouldIndex(D, /*IsRef=*/false))
      return false;

   SourceLoc Loc = D->getLoc(/*SerializedOK*/false);
   if (Loc.isInvalid() && !IsModuleFile)
      return false;

   if (!IsModuleFile) {
      if (!indexComment(D))
         return false;
   }

   IndexSymbol Info;
   if (auto FD = dyn_cast<FuncDecl>(D)) {
      if (initFuncDeclIndexSymbol(FD, Info))
         return false;
   } else {
      if (initIndexSymbol(D, Loc, /*IsRef=*/false, Info))
         return false;
   }

   for (auto Overriden: collectAllOverriddenDecls(D, /*IncludeInterfaceReqs=*/false)) {
      addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, Overriden);
   }

   if (auto Parent = getParentDecl()) {
      for (const IndexedWitness &witness : EntitiesStack.back().ExplicitWitnesses) {
         if (witness.Member == D)
            addRelation(Info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, witness.Requirement);
      }
      if (auto ParentVD = dyn_cast<ValueDecl>(Parent)) {
         SymbolRoleSet RelationsToParent = (SymbolRoleSet)SymbolRole::RelationChildOf;
         if (Info.symInfo.SubKind == SymbolSubKind::AccessorGetter ||
             Info.symInfo.SubKind == SymbolSubKind::AccessorSetter /*||
             (Info.symInfo.SubKind >= SymbolSubKind::SwiftAccessorWillSet &&
              Info.symInfo.SubKind <= SymbolSubKind::SwiftAccessorMutableAddressor)*/)
            RelationsToParent |= (SymbolRoleSet)SymbolRole::RelationAccessorOf;
         if (addRelation(Info, RelationsToParent, ParentVD))
            return false;

      } else if (auto ParentED = dyn_cast<ExtensionDecl>(Parent)) {
         if (ParentED->getExtendedNominal()) {
            if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationChildOf, ParentED))
               return false;
         }
      }
   }

   return startEntity(D, Info, /*IsRef=*/false);
}

bool IndexPHPAstWalker::reportRelatedRef(ValueDecl *D, SourceLoc Loc, bool isImplicit,
                                           SymbolRoleSet Relations, Decl *Related) {
   if (!shouldIndex(D, /*IsRef=*/true))
      return true;

   IndexSymbol Info;
   if (addRelation(Info, Relations, Related))
      return true;
   if (isImplicit)
      Info.roles |= (unsigned)SymbolRole::Implicit;

   // don't report this ref again when visitDeclReference reports it
   repressRefAtLoc(Loc);

   if (!reportRef(D, Loc, Info, None)) {
      Cancelled = true;
      return false;
   }

   return !Cancelled;
}

bool IndexPHPAstWalker::reportInheritedTypeRefs(ArrayRef<TypeLoc> Inherited, Decl *Inheritee) {
   for (auto Base : Inherited) {
      if (!reportRelatedTypeRef(Base, (SymbolRoleSet) SymbolRole::RelationBaseOf, Inheritee))
         return false;
   }
   return true;
}

bool IndexPHPAstWalker::reportRelatedTypeRef(const TypeLoc &Ty, SymbolRoleSet Relations, Decl *Related) {

   if (auto *T = dyn_cast_or_null<IdentTypeRepr>(Ty.getTypeRepr())) {
      auto Comps = T->getComponentRange();
      SourceLoc IdLoc = Comps.back()->getIdLoc();
      NominalTypeDecl *NTD = nullptr;
      bool isImplicit = false;
      if (auto *VD = Comps.back()->getBoundDecl()) {
         if (auto *TAD = dyn_cast<TypeAliasDecl>(VD)) {
            IndexSymbol Info;
            if (!reportRef(TAD, IdLoc, Info, None))
               return false;
            if (auto Ty = TAD->getUnderlyingType()) {
               NTD = Ty->getAnyNominal();
               isImplicit = true;
            }
         } else {
            NTD = dyn_cast<NominalTypeDecl>(VD);
         }
      }
      if (NTD) {
         if (!reportRelatedRef(NTD, IdLoc, isImplicit, Relations, Related))
            return false;
      }
      return true;
   }

   if (Ty.getType()) {
      if (auto nominal = Ty.getType()->getAnyNominal())
         if (!reportRelatedRef(nominal, Ty.getLoc(), /*isImplicit=*/false, Relations, Related))
            return false;
   }
   return true;
}

static bool isDynamicVarAccessorOrFunc(ValueDecl *D, SymbolInfo symInfo) {
   if (auto NTD = D->getDeclContext()->getSelfNominalTypeDecl()) {
      bool isClassOrInterface = isa<ClassDecl>(NTD) || isa<InterfaceDecl>(NTD);
//      bool isInternalAccessor =
//         symInfo.SubKind == SymbolSubKind::SwiftAccessorWillSet ||
//         symInfo.SubKind == SymbolSubKind::SwiftAccessorDidSet ||
//         symInfo.SubKind == SymbolSubKind::SwiftAccessorAddressor ||
//         symInfo.SubKind == SymbolSubKind::SwiftAccessorMutableAddressor;
      if (isClassOrInterface &&
          symInfo.Kind != SymbolKind::StaticMethod &&
          /*!isInternalAccessor &&*/
          !D->isFinal()) {
         return true;
      }
   }
   return false;
}

bool IndexPHPAstWalker::reportPseudoAccessor(AbstractStorageDecl *D,
                                               AccessorKind AccKind, bool IsRef,
                                               SourceLoc Loc) {
   if (!shouldIndex(D, IsRef))
      return true; // continue walking.

   auto updateInfo = [this, D, AccKind](IndexSymbol &Info) {
      if (getPseudoAccessorNameAndUSR(D, AccKind, Info.name, Info.USR))
         return true;
      Info.symInfo.Kind = SymbolKind::Function;
      if (D->getDeclContext()->isTypeContext()) {
         if (D->isStatic()) {
            if (D->getCorrectStaticSpelling() == StaticSpellingKind::KeywordClass)
               Info.symInfo.Kind = SymbolKind::ClassMethod;
            else
               Info.symInfo.Kind = SymbolKind::StaticMethod;
         } else {
            Info.symInfo.Kind = SymbolKind::InstanceMethod;
         }
      }
      Info.symInfo.SubKind = getSubKindForAccessor(AccKind);
      Info.roles |= (SymbolRoleSet)SymbolRole::Implicit;
      Info.group = "";
      if (isDynamicVarAccessorOrFunc(D, Info.symInfo)) {
         Info.roles |= (SymbolRoleSet)SymbolRole::Dynamic;
      }
      return false;
   };

   if (IsRef) {
      IndexSymbol Info;

      // initFuncRefIndexSymbol uses the top of the entities stack as the caller,
      // but in this case the top of the stack is the referenced
      // AbstractStorageDecl.
      assert(getParentDecl() == D);
      auto PreviousTop = EntitiesStack.pop_back_val();
      bool initFailed = initFuncRefIndexSymbol(D, Loc, Info);
      EntitiesStack.push_back(PreviousTop);

      if (initFailed)
         return true; // continue walking.
      if (updateInfo(Info))
         return true;

      if (!IdxConsumer.startSourceEntity(Info) || !IdxConsumer.finishSourceEntity(Info.symInfo, Info.roles))
         Cancelled = true;
   } else {
      IndexSymbol Info;
      if (initIndexSymbol(D, Loc, IsRef, Info))
         return true; // continue walking.
      if (updateInfo(Info))
         return true;
      if (addRelation(Info, (SymbolRoleSet)SymbolRole::RelationAccessorOf |
                            (SymbolRoleSet)SymbolRole::RelationChildOf , D))
         return true;

      if (!IdxConsumer.startSourceEntity(Info) || !IdxConsumer.finishSourceEntity(Info.symInfo, Info.roles))
         Cancelled = true;
   }
   return !Cancelled;
}

NominalTypeDecl *
IndexPHPAstWalker::getTypeLocAsNominalTypeDecl(const TypeLoc &Ty) {
   if (Type T = Ty.getType())
      return T->getAnyNominal();
   if (auto *T = dyn_cast_or_null<IdentTypeRepr>(Ty.getTypeRepr())) {
      auto Comp = T->getComponentRange().back();
      if (auto NTD = dyn_cast_or_null<NominalTypeDecl>(Comp->getBoundDecl()))
         return NTD;
   }
   return nullptr;
}

bool IndexPHPAstWalker::reportExtension(ExtensionDecl *D) {
   SourceLoc Loc = getLocForExtension(D);
   NominalTypeDecl *NTD = D->getExtendedNominal();
   if (!NTD)
      return true;
   if (!shouldIndex(NTD, /*IsRef=*/false))
      return true;

   IndexSymbol Info;
   if (initIndexSymbol(D, NTD, Loc, Info))
      return true;

   if (!startEntity(D, Info, /*IsRef=*/false))
      return false;

   if (!reportRelatedRef(NTD, Loc, /*isImplicit=*/false,
                         (SymbolRoleSet)SymbolRole::RelationExtendedBy, D))
      return false;
   if (!reportInheritedTypeRefs(D->getInherited(), D))
      return false;

   return true;
}

bool IndexPHPAstWalker::report(ValueDecl *D) {
   if (startEntityDecl(D)) {
      // Pass accessors.
      if (auto StoreD = dyn_cast<AbstractStorageDecl>(D)) {
         bool usedPseudoAccessors = false;
         if (isa<VarDecl>(D) &&
             !StoreD->getParsedAccessor(AccessorKind::Get) &&
             !StoreD->getParsedAccessor(AccessorKind::Set)) {
            usedPseudoAccessors = true;
            auto VarD = cast<VarDecl>(D);
            // No actual getter or setter, pass 'pseudo' accessors.
            // We create accessor entities so we can implement the functionality
            // of libclang, which reports implicit method property accessor
            // declarations, invocations, and overrides for properties.
            // Note that an ObjC class subclassing from a Swift class, may still
            // be able to override its non-computed-property-accessors via a
            // method.
            if (!reportPseudoGetterDecl(VarD))
               return false;
            if (!reportPseudoSetterDecl(VarD))
               return false;
         }

         for (auto accessor : StoreD->getAllAccessors()) {
            // Don't include the implicit getter and setter if we added pseudo
            // accessors above.
            if (usedPseudoAccessors &&
                (accessor->getAccessorKind() == AccessorKind::Get ||
                 accessor->getAccessorKind() == AccessorKind::Set))
               continue;

            ManuallyVisitedAccessorStack.push_back(accessor);
            SourceEntityWalker::walk(cast<Decl>(accessor));
            ManuallyVisitedAccessorStack.pop_back();
            if (Cancelled)
               return false;
         }
      } else if (auto NTD = dyn_cast<NominalTypeDecl>(D)) {
         if (!reportInheritedTypeRefs(NTD->getInherited(), NTD))
            return false;
      }
   } else {
      // Even if we don't record a local property we still need to walk its
      // accessor bodies.
      if (auto StoreD = dyn_cast<AbstractStorageDecl>(D)) {
         StoreD->visitParsedAccessors([&](AccessorDecl *accessor) {
            if (Cancelled)
               return;
            ManuallyVisitedAccessorStack.push_back(accessor);
            SourceEntityWalker::walk(cast<Decl>(accessor));
            ManuallyVisitedAccessorStack.pop_back();
         });
      }
   }

   return !Cancelled;
}

static bool hasUsefulRoleInSystemModule(SymbolRoleSet roles) {
   return roles & ((SymbolRoleSet)SymbolRole::Definition |
                   (SymbolRoleSet)SymbolRole::Declaration |
                   (SymbolRoleSet)SymbolRole::RelationChildOf |
                   (SymbolRoleSet)SymbolRole::RelationBaseOf |
                   (SymbolRoleSet)SymbolRole::RelationOverrideOf |
                   (SymbolRoleSet)SymbolRole::RelationExtendedBy |
                   (SymbolRoleSet)SymbolRole::RelationAccessorOf |
                   (SymbolRoleSet)SymbolRole::RelationIBTypeOf);
}

bool IndexPHPAstWalker::reportRef(ValueDecl *D, SourceLoc Loc,
                                    IndexSymbol &Info,
                                    Optional<AccessKind> AccKind) {
   if (!shouldIndex(D, /*IsRef=*/true))
      return true; // keep walking

   if (isa<AbstractFunctionDecl>(D)) {
      if (initFuncRefIndexSymbol(D, Loc, Info))
         return true;
   } else if (isa<AbstractStorageDecl>(D)) {
      if (initVarRefIndexSymbols(getCurrentExpr(), D, Loc, Info, AccKind))
         return true;
   } else {
      if (initIndexSymbol(D, Loc, /*IsRef=*/true, Info))
         return true;
   }

   if (isSystemModule && !hasUsefulRoleInSystemModule(Info.roles))
      return true;

   if (!startEntity(D, Info, /*IsRef=*/true))
      return true;

   // Report the accessors that were utilized.
   if (auto *ASD = dyn_cast<AbstractStorageDecl>(D)) {
      bool UsesGetter = Info.roles & (SymbolRoleSet)SymbolRole::Read;
      bool UsesSetter = Info.roles & (SymbolRoleSet)SymbolRole::Write;

      if (UsesGetter)
         if (!reportPseudoAccessor(ASD, AccessorKind::Get, /*IsRef=*/true,
                                   Loc))
            return false;
      if (UsesSetter)
         if (!reportPseudoAccessor(ASD, AccessorKind::Set, /*IsRef=*/true,
                                   Loc))
            return false;
   }

   return finishCurrentEntity();
}

bool IndexPHPAstWalker::reportImplicitConformance(ValueDecl *witness, ValueDecl *requirement,
                                                    Decl *container) {
   if (!shouldIndex(witness, /*IsRef=*/true))
      return true; // keep walking

   SourceLoc loc;
   if (auto *extD = dyn_cast<ExtensionDecl>(container))
      loc = getLocForExtension(extD);
   else
      loc = container->getLoc(/*SerializedOK*/false);

   IndexSymbol info;
   if (initIndexSymbol(witness, loc, /*IsRef=*/true, info))
      return true;
   if (addRelation(info, (SymbolRoleSet) SymbolRole::RelationOverrideOf, requirement))
      return true;
   if (addRelation(info, (SymbolRoleSet) SymbolRole::RelationContainedBy, container))
      return true;
   // Remove the 'ref' role that \c initIndexSymbol introduces. This isn't
   // actually a 'reference', but an 'implicit' override.
   info.roles &= ~(SymbolRoleSet)SymbolRole::Reference;
   info.roles |= (SymbolRoleSet)SymbolRole::Implicit;

   if (!startEntity(witness, info, /*IsRef=*/true))
      return true;
   return finishCurrentEntity();
}

bool IndexPHPAstWalker::initIndexSymbol(ValueDecl *D, SourceLoc Loc,
                                          bool IsRef, IndexSymbol &Info) {
   assert(D);
   if (Loc.isValid() && SrcMgr.findBufferContainingLoc(Loc) != BufferID)
      return true;
   if (auto *VD = dyn_cast<VarDecl>(D)) {
      // Always base the symbol information on the canonical VarDecl
      D = VD->getCanonicalVarDecl();
   }

   Info.decl = D;
   Info.symInfo = getSymbolInfoForDecl(D);
   if (Info.symInfo.Kind == SymbolKind::Unknown)
      return true;

   // Cannot be extension, which is not a ValueDecl.

   if (IsRef) {
      Info.roles |= (unsigned)SymbolRole::Reference;
      if (auto Container = getContainingDecl())
         addRelation(Info, (unsigned)SymbolRole::RelationContainedBy, Container);
   } else {
      Info.roles |= (unsigned)SymbolRole::Definition;
      if (D->isImplicit())
         Info.roles |= (unsigned)SymbolRole::Implicit;
   }

   if (getNameAndUSR(D, /*ExtD=*/nullptr, Info.name, Info.USR))
      return true;

   std::tie(Info.line, Info.column, Info.offset) = getLineColAndOffset(Loc);
   if (!IsRef) {
      if (auto Group = D->getGroupName())
         Info.group = Group.getValue();
   }
   return false;
}

bool IndexPHPAstWalker::initIndexSymbol(ExtensionDecl *ExtD, ValueDecl *ExtendedD,
                                          SourceLoc Loc, IndexSymbol &Info) {
   assert(ExtD && ExtendedD);
   Info.decl = ExtendedD;
   Info.symInfo = getSymbolInfoForDecl(ExtD);
   if (Info.symInfo.Kind == SymbolKind::Unknown)
      return true;

   Info.roles |= (unsigned)SymbolRole::Definition;

   if (getNameAndUSR(ExtendedD, ExtD, Info.name, Info.USR))
      return true;

   std::tie(Info.line, Info.column, Info.offset) = getLineColAndOffset(Loc);
   if (auto Group = ExtD->getGroupName())
      Info.group = Group.getValue();
   return false;
}

static NominalTypeDecl *getNominalParent(ValueDecl *D) {
   return D->getDeclContext()->getSelfNominalTypeDecl();
}

bool IndexPHPAstWalker::initFuncDeclIndexSymbol(FuncDecl *D,
                                                  IndexSymbol &Info) {
   if (initIndexSymbol(D, D->getLoc(/*SerializedOK*/false), /*IsRef=*/false, Info))
      return true;

   if (isDynamicVarAccessorOrFunc(D, Info.symInfo)) {
      Info.roles |= (SymbolRoleSet)SymbolRole::Dynamic;
   }

   if (D->hasImplicitSelfDecl()) {
      // If this is an @IBAction or @IBSegueAction method, find the sender
      // parameter (if present) and relate the method to its type.
      ParamDecl *senderParam = nullptr;

      auto paramList = D->getParameters();
      if (D->getAttrs().hasAttribute<IBActionAttr>()) {
         if (paramList->size() > 0)
            senderParam = paramList->get(0);
      } else if (D->getAttrs().hasAttribute<IBSegueActionAttr>()) {
         if (paramList->size() > 1)
            senderParam = paramList->get(1);
      }

      if (senderParam)
         if (auto nominal = senderParam->getType()->getAnyNominal())
            addRelation(Info, (SymbolRoleSet) SymbolRole::RelationIBTypeOf,
                        nominal);
   }

   if (auto Group = D->getGroupName())
      Info.group = Group.getValue();
   return false;
}

static bool isSuperRefExpr(Expr *E) {
   if (!E)
      return false;
   if (isa<SuperRefExpr>(E))
      return true;
   if (auto LoadE = dyn_cast<LoadExpr>(E))
      return isSuperRefExpr(LoadE->getSubExpr());
   return false;
}

static bool isDynamicCall(Expr *BaseE, ValueDecl *D) {
   // The call is 'dynamic' if the method is not of a struct/enum and the
   // receiver is not 'super'. Note that if the receiver is 'super' that
   // does not mean that the call is statically determined (an extension
   // method may have injected itself in the super hierarchy).
   // For our purposes 'dynamic' means that the method call cannot invoke
   // a method in a subclass.
   auto TyD = getNominalParent(D);
   if (!TyD)
      return false;
   if (isa<StructDecl>(TyD) || isa<EnumDecl>(TyD))
      return false;
   if (isSuperRefExpr(BaseE))
      return false;
   if (BaseE->getType()->is<MetatypeType>())
      return false;

   return true;
}

static bool isBeingCalled(Expr *Target, Expr *Parent, Expr *GrandParent) {
   if (!Target || !Parent || !isa<ApplyExpr>(Parent))
      return false;

   if (!isa<SelfApplyExpr>(Parent))
      return cast<ApplyExpr>(Parent)->getFn() == Target;

   return GrandParent && isa<CallExpr>(GrandParent) &&
          cast<CallExpr>(GrandParent)->getFn() == Parent;
}

bool IndexPHPAstWalker::initFuncRefIndexSymbol(ValueDecl *D, SourceLoc Loc,
                                                 IndexSymbol &Info) {

   if (initIndexSymbol(D, Loc, /*IsRef=*/true, Info))
      return true;

   Expr *CurrentE = getCurrentExpr();
   if (!CurrentE)
      return false;

   Expr *ParentE = getParentExpr();

   if (!isa<AbstractStorageDecl>(D) &&
       !isBeingCalled(CurrentE, ParentE, getContainingExpr(2)))
      return false;

   Info.roles |= (unsigned)SymbolRole::Call;
   if (auto *Caller = dyn_cast_or_null<AbstractFunctionDecl>(getParentDecl())) {
      if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationCalledBy, Caller))
         return true;
   }

   Expr *BaseE = nullptr;
   if (auto DotE = dyn_cast_or_null<DotSyntaxCallExpr>(ParentE))
      BaseE = DotE->getBase();
   else if (auto MembE = dyn_cast<MemberRefExpr>(CurrentE))
      BaseE = MembE->getBase();
   else if (auto SubsE = dyn_cast<SubscriptExpr>(CurrentE))
      BaseE = SubsE->getBase();

   if (!BaseE || BaseE == CurrentE)
      return false;

   if (Type ReceiverTy = BaseE->getType()) {
      if (auto LVT = ReceiverTy->getAs<LValueType>())
         ReceiverTy = LVT->getObjectType();
      else if (auto MetaT = ReceiverTy->getAs<MetatypeType>())
         ReceiverTy = MetaT->getInstanceType();

      if (auto TyD = ReceiverTy->getAnyNominal()) {
         if (addRelation(Info, (SymbolRoleSet) SymbolRole::RelationReceivedBy, TyD))
            return true;
         if (isDynamicCall(BaseE, D))
            Info.roles |= (unsigned)SymbolRole::Dynamic;
      }
   }

   return false;
}

bool IndexPHPAstWalker::initVarRefIndexSymbols(Expr *CurrentE, ValueDecl *D,
                                                 SourceLoc Loc, IndexSymbol &Info,
                                                 Optional<AccessKind> AccKind) {

   if (initIndexSymbol(D, Loc, /*IsRef=*/true, Info))
      return true;

   if (!CurrentE)
      return false;

   AccessKind Kind = AccKind.hasValue() ? *AccKind : AccessKind::Read;
   switch (Kind) {
      case polar::AccessKind::Read:
         Info.roles |= (unsigned)SymbolRole::Read;
         break;
      case polar::AccessKind::ReadWrite:
         Info.roles |= (unsigned)SymbolRole::Read;
         LLVM_FALLTHROUGH;
      case polar::AccessKind::Write:
         Info.roles |= (unsigned)SymbolRole::Write;
   }

   return false;
}

bool IndexPHPAstWalker::indexComment(const Decl *D) {
   // FIXME: Workaround for getting tag locations. We should enhance cmark to
   // keep track of node offsets in the original comment text.
   struct TagLoc {
      StringRef Text;
      SourceLoc Loc;
   };
   SmallVector<TagLoc, 3> tagLocs;
   for (const auto &single : D->getRawComment().Comments) {
      size_t idx = single.RawText.find("- Tag:");
      if (idx != StringRef::npos) {
         tagLocs.push_back(TagLoc{single.RawText,
                                  single.Range.getStart().getAdvancedLoc(idx)});
      }
   }
   if (tagLocs.empty())
      return true;
   /// TODO
//   polar::markup::MarkupContext MC;
//   auto DC = getSingleDocComment(MC, D);
//   if (!DC)
//      return true;
//   for (StringRef tagName : DC->getTags()) {
//      tagName = tagName.trim();
//      if (tagName.empty())
//         continue;
//      SourceLoc loc;
//      for (const auto &tagLoc : tagLocs) {
//         if (tagLoc.Text.contains(tagName)) {
//            loc = tagLoc.Loc;
//            break;
//         }
//      }
//      if (loc.isInvalid())
//         continue;
//      IndexSymbol Info;
//      Info.decl = nullptr;
//      // @todo
////      Info.symInfo = SymbolInfo{ SymbolKind::CommentTag, SymbolSubKind::None,
////                                 SymbolLanguage::Swift, SymbolPropertySet() };
//      Info.roles |= (unsigned)SymbolRole::Definition;
//      Info.name = StringRef();
//      SmallString<128> storage;
//      {
//         llvm::raw_svector_ostream OS(storage);
//         OS << "t:" << tagName;
//         Info.USR = stringStorage.copyString(OS.str());
//      }
//      std::tie(Info.line, Info.column, Info.offset) = getLineColAndOffset(loc);
//      if (!IdxConsumer.startSourceEntity(Info) || !IdxConsumer.finishSourceEntity(Info.symInfo, Info.roles)) {
//         Cancelled = true;
//         break;
//      }
//   }
//   return !Cancelled;
}

void IndexPHPAstWalker::getRecursiveModuleImports(
   ModuleDecl &Mod, SmallVectorImpl<ModuleDecl *> &Imports) {
   auto It = ImportsMap.find(&Mod);
   if (It != ImportsMap.end()) {
      Imports.append(It->second.begin(), It->second.end());
      return;
   }

   llvm::SmallPtrSet<ModuleDecl *, 16> Visited;
   collectRecursiveModuleImports(Mod, Visited);
   Visited.erase(&Mod);

   warn([&Imports](llvm::raw_ostream &OS) {
      std::for_each(Imports.begin(), Imports.end(), [&OS](ModuleDecl *M) {
         if (M->getModuleFilename().empty()) {
            std::string Info = "polar::ModuleDecl with empty file name!! \nDetails: \n";
            Info += "  name: ";
            Info += M->getName().get();
            Info += "\n";

            auto Files = M->getFiles();
            std::for_each(Files.begin(), Files.end(), [&](FileUnit *FU) {
               Info += "  file unit: ";

               switch (FU->getKind()) {
                  case FileUnitKind::Builtin:
                     Info += "builtin";
                     break;
                  case FileUnitKind::Source:
                     Info += "source, file=\"";
                     Info += cast<SourceFile>(FU)->getFilename();
                     Info += "\"";
                     break;
                  case FileUnitKind::SerializedAST:
                     Info += "serialized ast, file=\"";
                     Info += cast<LoadedFile>(FU)->getFilename();
                     Info += "\"";
                     break;
                  case FileUnitKind::ClangModule:
                  case FileUnitKind::DWARFModule:
                     Info += "clang module, file=\"";
                     Info += cast<LoadedFile>(FU)->getFilename();
                     Info += "\"";
               }

               Info += "\n";
            });

            OS << "polar::ModuleDecl with empty file name! " << Info << "\n";
         }
      });
   });

   Imports.append(Visited.begin(), Visited.end());
   std::sort(Imports.begin(), Imports.end(), [](ModuleDecl *LHS, ModuleDecl *RHS) {
      return LHS->getModuleFilename() < RHS->getModuleFilename();
   });

   // Cache it.
   ImportsMap[&Mod].append(Imports.begin(), Imports.end());
}

void IndexPHPAstWalker::collectRecursiveModuleImports(
   ModuleDecl &TopMod, llvm::SmallPtrSetImpl<ModuleDecl *> &Visited) {

   bool IsNew = Visited.insert(&TopMod).second;
   if (!IsNew)
      return;

   // Pure Clang modules are tied to their dependencies, no need to look into its
   // imports.
   // FIXME: What happens if the clang module imports a swift module ? So far
   // the assumption is that the path to the swift module will be fixed, so no
   // need to hash the clang module.
   // FIXME: This is a bit of a hack.
   if (TopMod.getFiles().size() == 1)
      if (TopMod.getFiles().front()->getKind() == FileUnitKind::ClangModule ||
          TopMod.getFiles().front()->getKind() == FileUnitKind::DWARFModule)
         return;

   auto It = ImportsMap.find(&TopMod);
   if (It != ImportsMap.end()) {
      Visited.insert(It->second.begin(), It->second.end());
      return;
   }

   ModuleDecl::ImportFilter ImportFilter;
   ImportFilter |= ModuleDecl::ImportFilterKind::Public;
   ImportFilter |= ModuleDecl::ImportFilterKind::Private;
   SmallVector<ModuleDecl::ImportedModule, 8> Imports;
   TopMod.getImportedModules(Imports);

   for (auto Import : Imports) {
      collectRecursiveModuleImports(*Import.second, Visited);
   }
}

//===----------------------------------------------------------------------===//
// Indexing entry points
//===----------------------------------------------------------------------===//

void index::indexDeclContext(DeclContext *DC, IndexDataConsumer &consumer) {
   assert(DC);
   unsigned bufferId = DC->getParentSourceFile()->getBufferID().getValue();
   IndexPHPAstWalker walker(consumer, DC->getAstContext(), bufferId);
   walker.visitDeclContext(DC);
   consumer.finish();
}

void index::indexSourceFile(SourceFile *SF, IndexDataConsumer &consumer) {
   assert(SF);
   unsigned bufferID = SF->getBufferID().getValue();
   IndexPHPAstWalker walker(consumer, SF->getAstContext(), bufferID);
   walker.visitModule(*SF->getParentModule());
   consumer.finish();
}

void index::indexModule(ModuleDecl *module, IndexDataConsumer &consumer) {
   assert(module);
   IndexPHPAstWalker walker(consumer, module->getAstContext());
   walker.visitModule(*module);
   consumer.finish();
}
